package com.candy.biz.manager;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.candy.biz.domain.entity.SysDict;
import com.candy.biz.domain.entity.SysDictItem;
import com.candy.biz.domain.entity.SysUser;
import com.candy.biz.domain.param.SysDictSaveParam;
import com.candy.biz.domain.param.SysUserSaveParam;
import com.candy.biz.domain.vo.SysDictItemVO;
import com.candy.biz.domain.vo.SysDictVO;
import com.candy.biz.mapper.SysDictItemMapper;
import com.candy.biz.mapper.SysDictMapper;
import com.candy.common.constant.CommonConstant;
import com.candy.common.domain.PageVO;
import com.candy.common.enums.AdminErrorEnum;
import com.candy.common.enums.RedisKeyEnum;
import com.candy.common.manager.RedisManager;
import com.candy.common.utils.Assert;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryChain;
import jakarta.annotation.PostConstruct;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import static com.candy.biz.domain.entity.table.SysDictItemTableDef.SYS_DICT_ITEM;
import static com.candy.biz.domain.entity.table.SysDictTableDef.SYS_DICT;

/**
 * @author rong xi
 * @version 1.0
 * @date 2023/09/18 23:48
 */
@Component
@AllArgsConstructor
@Slf4j
public class SysDictManager {

    private final SysDictMapper sysDictMapper;
    private final SysDictItemMapper sysDictItemMapper;
    private static ConcurrentHashMap<String, Map<String,String>> dictMap;
    private static SysDictManager manager;



    @PostConstruct
    private void init(){
        dictMap = new ConcurrentHashMap<>();
        manager = this;

    }

    /**
     * 字典分页对象转换分页视图数据
     *
     * @param page 分页结果
     * @return 分页数据
     */
    public static PageVO<SysDictVO> pageToPageVO(Page<SysDict> page) {
        //转换处理
        return PageVO.of(posToVos(page.getRecords()),page.getTotalRow());
    }


    /**
     * 参数转持久化对象
     */
    public static SysUser paramToPo(SysUserSaveParam param){
        return BeanUtil.copyProperties(param, SysUser.class);
    }


    /**
     * 字典po对象转字典vo对象
     *
     * @param po po对象
     * @return vo对象
     */
    public static SysDictVO poToVo(SysDict po){
        return CommonManager.convert(po, SysDictVO.class);
    }

    /**
     *字典po集合转vo集合
     *
     * @param pos po集合对象
     * @return vos vo集合对象
     */
    public static List<SysDictVO> posToVos(List<SysDict> pos){
        return CommonManager.convert(pos, SysDictVO.class);
    }

    /**
     *字典项po集合转字典项vo集合
     *
     * @param pos po集合对象
     * @return vos vo集合对象
     */
    public static List<SysDictItemVO> itemsToItemVos(List<SysDictItem> pos) {
        return CommonManager.convert(pos, SysDictItemVO.class);
    }

    /**
     * 插入字典准备
     *
     * @param param po
     */
    public static SysDict insertPrepare(SysDictSaveParam param){
        SysDict sysDict = BeanUtil.copyProperties(param, SysDict.class);
        //判断字典是否存在
        long count = manager.sysDictMapper.selectCountByCondition(SYS_DICT.DICT_CODE.eq(sysDict.getDictCode()));
        Assert.isTrue(count == 0,AdminErrorEnum.DICT_ALREADY_EXISTS);

        sysDict.setId(IdUtil.getSnowflakeNextId());
        sysDict.setCreateUser(StpUtil.getLoginIdAsLong());
        sysDict.setCreateTime(LocalDateTime.now());
        return sysDict;
    }

    /**
     * 修改字典准备
     *
     * @param param po
     */
    public static SysDict updatePrepare(SysDictSaveParam param){
        SysDict sysDict = BeanUtil.copyProperties(param, SysDict.class);
        sysDict.setUpdateUser(StpUtil.getLoginIdAsLong());
        sysDict.setUpdateTime(LocalDateTime.now());
        return sysDict;
    }

    /**
     * 获取字典项值
     *
     * @param dictCode 字典code
     * @param itemName 字典项
     * @return 字典项值
     */
    public static String getDictItemValue(String dictCode,String itemName){
        return getDictMapOpt(dictCode)
                .map(m->m.get(itemName))
                .orElse("");
    }

    /**
     * 获取字典项名称
     *
     * @param dictCode 字典code
     * @param itemValue 字典项值
     * @return 字典项名称
     */
    public static String getDictItemName(String dictCode,String itemValue){
        return getDictMapOpt(dictCode)
                .map(m->m.entrySet().stream()
                        .filter(e->e.getValue().equals(itemValue)).findAny().map(Map.Entry::getKey).orElse(""))
                .orElse("");
    }

    /**
     * 获取字典项值
     *
     * @param dictCode 字典code
     * @return 字典项map
     */
    public static Map<String,String> getDictMap(String dictCode){
        return getDictMapOpt(dictCode).orElseGet(Collections::emptyMap);
    }

    /**
     * 查询字典map
     *
     * @param dictCode 字典code
     * @return 字典map
     */
    public static Optional<Map<String,String>> getDictMapOpt(String dictCode){

        return Optional.of(dictMap)
                .map(e->e.get(dictCode))
                .filter(CollectionUtil::isNotEmpty)
                .or(()-> Optional.of(RedisManager.<String>getMap(RedisKeyEnum.DICT_MAP.getKey(dictCode))))
                .filter(CollectionUtil::isNotEmpty)
                .or(()-> Optional.of(QueryChain.of(manager.sysDictItemMapper).select(SYS_DICT_ITEM.ITEM_NAME,SYS_DICT_ITEM.ITEM_VALUE)
                                .from(SYS_DICT_ITEM)
                                .where(SYS_DICT_ITEM.DICT_CODE.eq(dictCode))
                                .and(SYS_DICT_ITEM.STATE.eq(CommonConstant.ZERO))
                                .list().stream().collect(Collectors.toMap(SysDictItem::getItemName, SysDictItem::getItemValue)))
                        .filter(CollectionUtil::isNotEmpty)
                        .map(m->{
                            //缓存到1级
                            dictMap.put(dictCode,m);
                            //缓存到2级
                            RedisManager.setMap(RedisKeyEnum.DICT_MAP.getKey(dictCode), m);
                            RedisManager.expire(RedisKeyEnum.DICT_MAP.getKey(dictCode),RedisKeyEnum.DICT_MAP.getTimeout());
                            return m;
                        })
                );
    }
}
